package server;

import server.handler.SelectionKeyHandlerChain;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.Iterator;

/**
 * @author srh
 * @date 2020/10/18
 **/
public class NIOServer extends Thread {

    private static final Integer PORT = 8888;

    private SelectionKeyHandlerChain selectionKeyHandlerChain;
    private Selector selector;

    public NIOServer() {
        init(PORT);

        selectionKeyHandlerChain = new SelectionKeyHandlerChain(this.selector);
    }

    private void init(Integer port) {
        try {
            // 开启多路复用器
            this.selector = Selector.open();
            // 开启服务通道
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            // 设置为非阻塞
            serverSocketChannel.configureBlocking(false);
            // 绑定端口
            serverSocketChannel.bind(new InetSocketAddress(port));
            // 注册到多路复用器，且标记状态为可连接状态
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        while (true) {
            try {
                // 当至少有一个通道被选中时，才执行此方法
                this.selector.select();
                // 获取选中通道到编码集合
                Iterator<SelectionKey> keys = this.selector.selectedKeys().iterator();
                while (keys.hasNext()) {
                    SelectionKey selectionKey = keys.next();
                    // 消费此 key，避免重复消费
                    keys.remove();
                    // 判断通道是否有效
                    if (!selectionKey.isValid()) {
                        throw new RuntimeException("通道无效 key=" + selectionKey);
                    }
                    // 消费 selectionKey
                    selectionKeyHandlerChain.consumer(selectionKey);
                }
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        new Thread(new NIOServer()).start();
    }
}
